using System;
using System.Collections.Generic;
using System.Text;
using Watch = System.Diagnostics.Stopwatch;

namespace Zsa.Emulation
{
    /// <summary>
    /// Used for timing.
    /// </summary>
    public class TimingCookie
    {
        #region Static 

        /// <summary>
        /// Creates from seconds.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static TimingCookie CreateFromSeconds(double value)
        {
            return CreateFromTimespan(TimeSpan.FromSeconds(value));
        }

        /// <summary>
        /// Creates from milliseconds.
        /// </summary>
        /// <param name="milliSecond">The amount of time for a single operation in miliseconds..</param>
        /// <returns></returns>
        public static TimingCookie CreateFromMilliseconds(double value)
        {
            return CreateFromTimespan(TimeSpan.FromSeconds(value));
        }

        /// <summary>
        /// Create a timing cookie from a timespan.
        /// </summary>
        /// <param name="span">The amount of time for a single operation.</param>
        /// <returns></returns>
        public static TimingCookie CreateFromTimespan(TimeSpan value)
        {
            return new TimingCookie(
                (float)Math.Round(value.TotalMilliseconds, 7)
            );
        }

        #endregion Static

        #region Fields
        /// <summary>
        /// The required operations per second.
        /// </summary>
        private readonly float _RequiredOps;

        /// <summary>
        /// The timer watch.
        /// </summary>
        private readonly Watch _Watch;

        /// <summary>
        /// The current operations that have occured.
        /// </summary>
        private int _CurrentOperationCount;

        /// <summary>
        /// The number of seconds before the timer should be reset.
        /// </summary>
        private int _ResetMilliseconds = 10000; // Ten second average.
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="T:TimingCookie"/> class.
        /// </summary>
        /// <param name="operationsPerMillsecond">The length of single operation in milliseconds.</param>
        public TimingCookie(float operationsPerMillsecond)
        {
            _RequiredOps = operationsPerMillsecond;
            _Watch = new System.Diagnostics.Stopwatch();
        }
        #endregion Constructor

        #region Properties
        /// <summary>
        /// Gets the current operations.
        /// </summary>
        /// <value>The current operations.</value>
        public int CurrentOperations
        {
            get
            {
                return _CurrentOperationCount;
            }
        }

        /// <summary>
        /// The requested operations per second.
        /// </summary>
        /// <value>The operations per second.</value>
        public float RequiredOpsPerSecond
        {
            get
            {
                return _RequiredOps*1000F;
            }
        }

        /// <summary>
        /// Gets the operations per milli second.
        /// </summary>
        /// <value>The operations per milli second.</value>
        public float RequiredOpsPerMillisecond
        {
            get
            {
                return _RequiredOps;
            }
        }

        /// <summary>
        /// Gets the current ops per millisecond.
        /// </summary>
        /// <value>The current ops per millisecond.</value>
        public float CurrentOpsPerMillisecond
        {
            get
            {
                return _Watch.ElapsedMilliseconds /
                    _CurrentOperationCount;
            }
        }

        /// <summary>
        /// Gets the current ops per second.
        /// </summary>
        /// <value>The current ops per second.</value>
        public float CurrentOpsPerSecond
        {
            get
            {
                return CurrentOpsPerMillisecond * 1000F;
            }
        }

        /// <summary>
        /// Gets the speed.
        /// </summary>
        /// <value>The speed.</value>
        public float Speed
        {
            get
            {
                return CurrentOpsPerMillisecond / _RequiredOps;
            }
        }
        
        #endregion Properties

        #region Methods
        /// <summary>
        /// Starts the timer.
        /// </summary>
        private void Start()
        {
            _CurrentOperationCount = 0;
            _Watch.Reset();
            _Watch.Start();
        }

        /// <summary>
        /// Resets this instance.
        /// </summary>
        public void Reset()
        {
            _Watch.Reset();
            _CurrentOperationCount = 0;
        }

        /// <summary>
        /// Updates the number of values
        /// </summary>
        /// <param name="ops"></param>
        /// <returns></returns>
        public int Update(int ops)
        {
            // Add the operations.
            _CurrentOperationCount += ops;

            // The number of operations that should have happened in the elapsed time.
            int _OpDifference = (int)
                (Math.Round((_Watch.ElapsedMilliseconds / _RequiredOps)) - _CurrentOperationCount);

            // If greater then the reset limit then reset.
            if (_Watch.ElapsedMilliseconds > _ResetMilliseconds)
            {
                _Watch.Reset();
                _CurrentOperationCount = 0;
            }
            // Returns the ops behind.
            return _OpDifference;

        }
        #endregion Methods
    }
}
